package org.fxkj.cloud.uaa.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.fxkj.cloud.uaa.constants.RoleEnum;
import org.fxkj.cloud.uaa.service.security.WAuthenticationProvider;
import org.fxkj.cloud.uaa.service.security.WRefreshAuthenticationProvider;
import org.fxkj.cloud.uaa.service.security.filter.WAuthenticationFilter;
import org.fxkj.cloud.uaa.service.security.filter.WRefreshAuthenticationFilter;
import org.fxkj.cloud.uaa.service.security.handler.*;
import org.fxkj.cloud.uaa.service.security.model.token.JwtTokenFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;

/**
 * <p>Description: 安全配置类</p>
 *
 * @author Wei, Wu
 * @create 2024/10/15 11:28
 */
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfiguration {

    //权限不足处理入口
    private final WAccessDeniedHandler accessDeniedHandler;

    //未登录处理入口
    private final WUnLoginEntryPoint unLoginEntryPoint;

    //登陆成功处理器
    private final LoginSuccessHandler loginSuccessHandler;

    //登陆失败处理器
    private final LoginFailureHandler loginFailureHandler;

    //退出登录处理器
    private final WLogoutHandler logoutHandler;

    private final WAuthenticationProvider authenticationProvider;
    private final WRefreshAuthenticationProvider refreshAuthenticationProvider;

    @Autowired
    private AuthenticationManager authenticationManager;

    private final JwtTokenFactory jwtTokenFactory;
    private final ObjectMapper objectMapper;
    private final WXLoginSuccessHandler wxLoginSuccessHandler;

    @Bean
    public WAuthenticationFilter buildWAuthenticationFilter() {
        WAuthenticationFilter wAuthenticationFilter = new WAuthenticationFilter(new AntPathRequestMatcher("/api/**"));
        wAuthenticationFilter.setAuthenticationManager(authenticationManager);
        return wAuthenticationFilter;
    }

    @Bean
    public WRefreshAuthenticationFilter buildWRefreshAuthenticationFilter() {
        WRefreshAuthenticationFilter wRefreshAuthenticationFilter = new WRefreshAuthenticationFilter(new AntPathRequestMatcher(WRefreshAuthenticationFilter.REFRESH_TOKEN_URI), jwtTokenFactory, objectMapper);
        wRefreshAuthenticationFilter.setAuthenticationManager(authenticationManager);
        return wRefreshAuthenticationFilter;
    }


    @Bean("firstSecurityFilterChain")
    @Order(Ordered.HIGHEST_PRECEDENCE)
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                //关闭csrf
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(request -> {
                    //放行swagger相关
                    request
                            .requestMatchers("/ws/**","/swagger-ui/**", "/favicon.ico", "/v3/**", "/wx/**", "/refreshToken", "/api/emqx/**", "/wx/login/**").permitAll()
                            .requestMatchers("/api/admin/**").hasRole(RoleEnum.ROLE_ADMIN.getName())
                            //其他请求 必须要验证
                            .anyRequest().authenticated();
                })
                //无状态session 前后端分离,禁用session
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                //表单登陆
                .formLogin(
                        form -> {
                            form.loginPage("/login").permitAll();
                            form.usernameParameter("username");
                            form.passwordParameter("password");
                            form.successHandler(loginSuccessHandler);
                            form.failureHandler(loginFailureHandler);
                        }
                )
                .exceptionHandling(httpSecurityExceptionHandlingConfigurer -> {
                    //权限不足处理
                    httpSecurityExceptionHandlingConfigurer.accessDeniedHandler(accessDeniedHandler);
                    //未登录处理入口
                    httpSecurityExceptionHandlingConfigurer.authenticationEntryPoint(unLoginEntryPoint);
                })
                .logout(logoutConfigurer -> {
                    logoutConfigurer.logoutUrl("/logout");
                    logoutConfigurer.addLogoutHandler(logoutHandler);
                    logoutConfigurer.clearAuthentication(true);
                })
                .headers(httpSecurityHeadersConfigurer -> {
                    // 禁用缓存
                    httpSecurityHeadersConfigurer.cacheControl(HeadersConfigurer.CacheControlConfig::disable);
                    httpSecurityHeadersConfigurer.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable);
                })
        ;
        http.addFilterBefore(buildWAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(buildWRefreshAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
        ;
        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(ObjectPostProcessor<Object> objectPostProcessor) throws Exception {
        DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
                .postProcess(new DefaultAuthenticationEventPublisher());
        var auth = new AuthenticationManagerBuilder(objectPostProcessor);
        auth.authenticationEventPublisher(eventPublisher);
        auth.authenticationProvider(authenticationProvider);
        auth.authenticationProvider(refreshAuthenticationProvider);
        return auth.build();
    }

    @Bean
    BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     *  跨域支持
     * @return CorsConfigurationSource
     */
    @Bean
    protected CorsConfigurationSource corsConfigurationSource() {

        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.addExposedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
